using System;
using System.Xml;

namespace XmlObjects
{
	public delegate XmlNode QueryCreateNode(string selectString, XmlNode node);

	public class AttribAttribute : PropertyAttribute
	{
		public AttribAttribute(string name) : base("@" + name)
		{
			this.SelectString = "@" + name;
			this.nodeName = name;
		}

		public override XmlNode QueryCreateNode(string name, XmlNode parent)
		{
			foreach (XmlAttribute attribute in parent.Attributes)
			{
				if (attribute.Name == name)
					return attribute;
			}
			return parent.Attributes.Append(parent.OwnerDocument.CreateAttribute(name));
		}

		/// <summary>
		/// Describes a property of an object which should be treated as an attribute of an Xml document.
		/// </summary>
		/// <param name="name">the name of the attribute</param>
		/// <param name="format">optional format string for datetime, etc.</param>
		public AttribAttribute(string name, string format) : base(name, format)
		{
		}
	}

	[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
	public class SubTypeAttribute : Attribute
	{
		public Type Type;
		public string Id;
		public bool IsDefault = false;

		public virtual XmlNode QueryCreateNode(string name, XmlNode parent)
		{
			XmlNode node = parent.SelectSingleNode(name);
			if (null != node)
				return node;
			else
			{
				return parent.AppendChild(parent.OwnerDocument.CreateElement(name));
			}
		}

		public SubTypeAttribute(string id, Type type)
		{
			Type = type;
			Id = id;
		}

		public SubTypeAttribute(string key, Type type, bool isDefault)
		{
			Type = type;
			Id = key;
			IsDefault = isDefault;
		}
	}

	[AttributeUsage(AttributeTargets.Property)]
	public class PropertyAttribute : Attribute
	{
		public string SelectString;
		public string FindSubTypeBy = "#name";

		public string NodeName
		{
			get { return nodeName; }
		}

		protected string nodeName;

		public virtual XmlNode QueryCreateNode(string name, XmlNode parent)
		{
			XmlNode node = parent.SelectSingleNode(name);
			if (null!=node)
				return node;
			else
			{
				return parent.AppendChild(parent.OwnerDocument.CreateElement(name));
			}
		}

		/// <summary>
		/// Describes a property of an object which should be treated as an Element node in an Xml document.
		/// </summary>
		/// <param name="select">an XPath expression describing the location of this object with respect to the current node.</param>
		/// <param name="findSubTypeBy">An optional format string for datimes, etc, which require formatting information.</param>
		public PropertyAttribute(string select, string findSubTypeBy) : this(select)
		{
			FindSubTypeBy = findSubTypeBy;
		}

		/// <summary>
		/// Describes a property of an object which should be treated as an Element node in an Xml document.
		/// </summary>
		/// <param name="select">an XPath expression describing the location of this object with respect to the current node.</param>
		public PropertyAttribute(string select)
		{
			SelectString = select;
			string[] strings = select.Split('/');
			nodeName = strings[strings.Length - 1];
		}
	}
}